#pragma once

#include <mcp/config.h>
#include <mcp/forward.h>
#include <mcp/types.h>
#include <mcp/error.h>

#include <memory>
#include <string>
#include <vector>
#include <optional>
#include <functional>
#include <coroutine>

namespace mcp {
namespace server {

/// @brief Forward declarations
class ToolManager;
class ResourceManager;
class PromptManager;
class Transport;

/// @brief Server session for handling client connections
class ServerSession : public std::enable_shared_from_this<ServerSession> {
public:
    explicit ServerSession(std::unique_ptr<Transport> transport,
                          std::shared_ptr<ToolManager> tool_manager,
                          std::shared_ptr<ResourceManager> resource_manager,
                          std::shared_ptr<PromptManager> prompt_manager);
    ~ServerSession();

    /// @brief Start the session
    /// @return true if started successfully, false otherwise
    bool start();

    /// @brief Stop the session
    void stop();

    /// @brief Check if session is active
    /// @return true if session is active, false otherwise
    bool is_active() const;

    /// @brief Send a response to the client
    /// @param request_id Request ID to respond to
    /// @param result Response result
    /// @return true if sent successfully, false otherwise
    bool send_response(types::RequestId request_id, const json::Value& result);

    /// @brief Send an error to the client
    /// @param request_id Request ID that caused the error
    /// @param error Error information
    /// @return true if sent successfully, false otherwise
    bool send_error(types::RequestId request_id, const Error& error);

    /// @brief Send a notification to the client
    /// @param notification Notification to send
    /// @return true if sent successfully, false otherwise
    bool send_notification(const std::string& method, const json::Value& params);

    /// @brief Log a message
    /// @param message Logging message to send
    void log(const types::LoggingMessage& message);

    /// @brief Send progress notification
    /// @param progress Progress notification to send
    void send_progress(const types::ProgressNotification& progress);

    /// @brief Read a resource
    /// @param uri Resource URI to read
    /// @return Resource content or error
    Result<types::ResourceContent> read_resource(const std::string& uri);

    /// @brief Elicit user input
    /// @param message Message to show user
    /// @param schema Schema for expected input
    /// @return Elicitation result or error
    template<typename SchemaT>
    Result<types::ElicitationResult> elicit(const std::string& message, const SchemaT& schema);

    /// @brief Create message using LLM sampling
    /// @param messages Message history
    /// @param options Sampling options
    /// @return Sampling result or error
    Result<types::SamplingResult> create_message(
        const std::vector<types::SamplingMessage>& messages,
        const types::SamplingOptions& options = {});

    /// @brief Get session ID
    /// @return Unique session identifier
    const std::string& session_id() const;

    /// @brief Get session capabilities
    /// @return Session capabilities supported by this session
    const json::Value& capabilities() const;

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

// Template implementation for elicit method
template<typename SchemaT>
Result<types::ElicitationResult> ServerSession::elicit(const std::string& message, const SchemaT& schema) {
    // Convert schema to JSON value
    auto schema_json = json::Value::object({
        {"type", "object"},
        {"properties", json::Value::object({})},
        {"required", json::Value::array({})}
    });

    // This is a simplified implementation - in a real implementation,
    // you'd convert the SchemaT to a proper JSON schema
    return Error::unsupported_operation("Elicitation not implemented in this session");
}

/// @brief Server for handling multiple sessions
class Server {
public:
    Server(std::shared_ptr<ToolManager> tool_manager,
           std::shared_ptr<ResourceManager> resource_manager,
           std::shared_ptr<PromptManager> prompt_manager);
    ~Server();

    /// @brief Create a new session with the given transport
    /// @param transport Transport for the session
    /// @return Shared pointer to the new session
    std::shared_ptr<ServerSession> create_session(std::unique_ptr<Transport> transport);

    /// @brief Get all active sessions
    /// @return List of active sessions
    std::vector<std::weak_ptr<ServerSession>> get_active_sessions() const;

    /// @brief Get session count
    /// @return Number of active sessions
    size_t session_count() const;

    /// @brief Get server capabilities
    /// @return Server capabilities
    json::Value get_capabilities() const;

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

} // namespace server
} // namespace mcp